using FastDevTemplate.Common.Util;
using FastDevTemplate.DataAccess;
using FastDevTemplate.DataAccess.Entities;
using FastDevTemplate.Service.Base;
using FastDevTemplate.Service.Interface;
using FastDevTemplate.Service.Interface.DTO;
using Microsoft.EntityFrameworkCore;

namespace FastDevTemplate.Service;

public class UserService(FastDevTemplateDbContext dbContext) :
    GuidAuditCurdService<
        User,
        UserResultDto,
        UserGetResultDto,
        UserAddDto,
        UserUpdateDto>(dbContext),
    IUserService
{
    public async Task<UserGetResultDto?> GetByUserNameAsync(string userName)
    {
        var entity = await DbSet
            .FirstOrDefaultAsync(item => item.UserName == userName);
        return ObjectMapper.Map<UserGetResultDto>(entity);
    }

    public override async Task<UserResultDto?> AddAsync(UserAddDto commandDto)
    {
        commandDto.Password = EncryptionTool.ComputeSha1Hash(commandDto.Password);
        return await base.AddAsync(commandDto);
    }

    public override Task<UserResultDto?> ModifyAsync(Guid id, UserUpdateDto commandDto)
    {
        commandDto.Password = EncryptionTool.ComputeSha1Hash(commandDto.Password);
        return base.ModifyAsync(id, commandDto);
    }

    public async Task<int> UpdatePasswordAsync(Guid userId, UserUpdatePasswordDto dto)
    {
        dto.NewPassword = EncryptionTool.ComputeSha1Hash(dto.NewPassword);
        dto.OldPassword = EncryptionTool.ComputeSha1Hash(dto.OldPassword);

        var entity = await DbSet.FindAsync(userId);
        if (entity == null || entity.Password != dto.OldPassword)
            return ErrorHandling(ErrorType.ArgumentVerifyError);
        entity.Password = dto.NewPassword;
        try
        {
            var count = await Dao.SaveChangesAsync();
            if (count <= 0)
                return ErrorHandling(ErrorType.DataBaseSaveError, nameof(UpdatePasswordAsync));
            return count;
        }
        catch (Exception e)
        {
            return ErrorHandling(ErrorType.DataBaseSaveError, e);
        }
    }

    public async Task<UserLoginDto?> LoginAsync(string username, string password)
    {
        password = EncryptionTool.ComputeSha1Hash(password);

        //logic:
        var userEntity = await DbSet
            .Include(u => u.UserRoles)
            .ThenInclude(ur => ur.Role)
            .ThenInclude(r => r.RolePermissions)
            .FirstOrDefaultAsync(e => e.UserName == username && e.Password == password);

        if (userEntity == null)
        {
            ErrorHandling(ErrorType.LogicNotFindEntityError, nameof(LoginAsync));
            return null;
        }

        userEntity.ToLoginState();
        if (await Dao.SaveChangesAsync() <= 0)
        {
            ErrorHandling(ErrorType.DataBaseSaveError, nameof(LoginAsync));
            return null;
        }
        
        var dto = ObjectMapper.Map<UserLoginDto>(userEntity);
        return dto;
    }

    public async Task<ICollection<RoleResultDto>> GetRolesByUserIDAsync(Guid userId)
    {
        var roles = await Dao.Set<Role>()
            .Where(item => item.UserRoles.Any(ur => ur.UserID == userId))
            .ToListAsync();
        return roles.Select(item => ObjectMapper.Map<RoleResultDto>(item)).ToList();
    }

    public async Task<int> SetRoleAsync(Guid id, params string[] roleKeys)
    {
        //Check:
        var entity = await DbSet.Include(item => item.UserRoles).FirstOrDefaultAsync(item => item.ID == id);
        if (entity == null)
            return ErrorHandling(ErrorType.LogicNotFindEntityError, nameof(SetRoleAsync));

        //Logic:
        await using var transaction = await Dao.Database.BeginTransactionAsync();
        try
        {
            int count = 0;
            entity.SetRoles(roleKeys);
            Dao.Update(entity);
            count += await Dao.SaveChangesAsync();
            await transaction.CommitAsync();
            return count > 0 ? count : ErrorHandling(ErrorType.DataBaseSaveError, nameof(SetRoleAsync));
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            return ErrorHandling(ErrorType.DataBaseSaveError, e);
        }
    }

    public async Task<int> AddRoleListAsync(Guid id, params string[] roleKeys)
    {
        //Check:
        var entity = await DbSet.Include(item => item.UserRoles).FirstOrDefaultAsync(item => item.ID == id);
        if (entity == null)
            return ErrorHandling(ErrorType.LogicNotFindEntityError, nameof(AddRoleListAsync));

        //logic:
        await using var transaction = await Dao.Database.BeginTransactionAsync();
        foreach (var pk in roleKeys)
            entity.AddRole(pk);
        DbSet.Update(entity);

        //Error Handle:
        try
        {
            var count = await Dao.SaveChangesAsync();
            await transaction.CommitAsync();
            return count > 0 ? count : ErrorHandling(ErrorType.DataBaseSaveError, nameof(AddRoleListAsync));
        }
        catch (Exception e)
        {
            await transaction.RollbackAsync();
            return ErrorHandling(ErrorType.DataBaseSaveError, e);
        }
    }

    public async Task<int> RemoveRoleListAsync(Guid id, params string[] roleKeys)
    {
        //Check:
        var entity = await DbSet.Include(item => item.UserRoles).FirstOrDefaultAsync(item => item.ID == id);
        if (entity == null)
            return ErrorHandling(ErrorType.LogicNotFindEntityError, nameof(RemoveRoleListAsync));

        //logic:
        await using var transaction = await Dao.Database.BeginTransactionAsync();
        foreach (var pk in roleKeys)
            entity.RemoveRole(pk);
        DbSet.Update(entity);

        //Error Handle:
        try
        {
            var count = await Dao.SaveChangesAsync();
            await transaction.CommitAsync();
            return count > 0 ? count : ErrorHandling(ErrorType.DataBaseSaveError, nameof(RemoveRoleListAsync));
        }
        catch (Exception e)
        {
            await transaction.RollbackAsync();
            return ErrorHandling(ErrorType.DataBaseSaveError, e);
        }
    }

    protected override async ValueTask<User?> EntityFindAsync(Guid id)
    {
        return await DbSet.Include(item => item.UserRoles).FirstOrDefaultAsync(item => item.ID == id);
    }
}